package cn.edu.gzgs.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Component
public class JwtUtils {

    // 从 application.yml 读取密钥。请确保在 yml 文件中配置 jwt.secret
    // 密钥长度至少需要满足所选签名算法的要求 (例如 HS256 需要至少 256 位/32 字节)
    // 强烈建议使用更长、更随机的密钥
    @Value("${jwt.secret:默认密钥DefaultSecretKeyForHS256Algorithm}") // 提供一个默认值以防配置缺失
    private String secret;

    // 从 application.yml 读取过期时间（毫秒）。请确保在 yml 文件中配置 jwt.expiration
    @Value("${jwt.expiration:86400000}") // 默认 24 小时
    private Long expiration;

    private SecretKey getSigningKey() {
        // 使用 Keys.hmacShaKeyFor 来安全地生成基于 secret 字符串的 SecretKey
        // 注意：每次调用 getSigningKey 都会根据当前的 secret 生成密钥实例。
        // 如果 secret 不变，生成的 key 也是一样的。
        // 如果 secret 经常变动，需要考虑缓存 key。
        // 确保 secret 的字节长度满足 HS256 (>=32 bytes), HS384 (>=48 bytes), 或 HS512 (>=64 bytes) 的要求
        byte[] keyBytes = secret.getBytes();
        // 对于 HS256，至少需要 32 字节。如果少于这个长度，Keys.hmacShaKeyFor 会报错。
        // 如果 secret 较短，可以考虑使用其他方法生成或填充 keyBytes。
        // 例如： Base64.getDecoder().decode(base64EncodedSecret) 如果secret是Base64编码的
        // 或者使用更安全的密钥管理方式
        return Keys.hmacShaKeyFor(keyBytes);
    }

    // 从 Token 中提取用户名 (或者用户ID，取决于存储的是什么)
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    // 从 Token 中提取过期时间
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    // 从 Token 中提取指定的 Claim
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    // 解析 Token 中的所有 Claims
    private Claims extractAllClaims(String token) {
        return Jwts.parserBuilder()
                   .setSigningKey(getSigningKey())
                   .build()
                   .parseClaimsJws(token)
                   .getBody();
    }

    // 检查 Token 是否过期
    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    // 生成 Token
    public String generateToken(String username) { // 通常传入用户唯一标识，如 userId 或 username
        Map<String, Object> claims = new HashMap<>();
        // 可以在 claims 中添加其他非敏感信息，如角色等
        // claims.put("roles", rolesList);
        return createToken(claims, username);
    }

    // 创建 Token 的核心逻辑
    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                   .setClaims(claims)
                   .setSubject(subject) // 通常存放用户唯一标识
                   .setIssuedAt(new Date(System.currentTimeMillis()))
                   .setExpiration(new Date(System.currentTimeMillis() + expiration))
                   .signWith(getSigningKey(), SignatureAlgorithm.HS256) // 或 HS384, HS512
                   .compact();
    }

    // 验证 Token 是否有效 (检查是否过期以及签名是否正确)
    public Boolean validateToken(String token, String username) { // 验证时可能需要比对 Token 中的 subject 与当前尝试操作的用户是否一致
        final String extractedUsername = extractUsername(token);
        return (extractedUsername.equals(username) && !isTokenExpired(token));
    }

    // 更通用的验证方法，只检查签名和过期时间
    public Boolean validateToken(String token) {
         try {
            extractAllClaims(token); // 尝试解析，如果签名错误或过期会抛异常
            return !isTokenExpired(token);
         } catch (Exception e) { // 可以捕获更具体的异常，如 SignatureException, ExpiredJwtException 等
            return false;
         }
    }
} 